{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "7800fe10-6a93-494c-8e96-498f89d4220a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Downloading https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz to ./Data\\cifar-10-python.tar.gz\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100.0%\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Extracting ./Data\\cifar-10-python.tar.gz to ./Data\n",
      "Test set size: 10000\n",
      "Files already downloaded and verified\n",
      "Training set size: 50000\n",
      "Number of training samples: 40000\n",
      "Number of cross-validation samples: 10000\n"
     ]
    }
   ],
   "source": [
    "import torch\n",
    "import torchvision\n",
    "import torchvision.transforms as transforms\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "label_size = 18 # Label size\n",
    "ticklabel_size = 14 # Tick label size\n",
    "    \n",
    "# Define a transform to normalize the data\n",
    "transform = transforms.Compose([\n",
    "    transforms.ToTensor()\n",
    "])\n",
    "\n",
    "# Load test data from the MNIST\n",
    "testset = torchvision.datasets.CIFAR10(root='./Data', train=False, download=True, transform=transform)\n",
    "print(f\"Test set size: {len(testset)}\")\n",
    "\n",
    "# Load training data from the MNIST\n",
    "trainset = torchvision.datasets.CIFAR10(root='./Data', train=True, download=True, transform=transform)\n",
    "print(f\"Training set size: {len(trainset)}\")\n",
    "\n",
    "# Rate of trX and cvX\n",
    "tr_cv_rate = 0.8\n",
    "\n",
    "# Create a list to store indices for each class unique()\n",
    "class_indices = [[] for _ in range(10)]  # 10 classes in MNIST\n",
    "\n",
    "# Populate class_indices\n",
    "for idx, (_, label) in enumerate(trainset):\n",
    "    class_indices[label].append(idx)\n",
    "\n",
    "# Calculate the number of samples for each class in training and validation sets\n",
    "train_size_per_class = int(tr_cv_rate * min(len(indices) for indices in class_indices))\n",
    "val_size_per_class = min(len(indices) for indices in class_indices) - train_size_per_class\n",
    "\n",
    "# Create balanced train and validation sets\n",
    "train_indices = []\n",
    "val_indices = []\n",
    "for indices in class_indices:\n",
    "    train_indices.extend(indices[:train_size_per_class])\n",
    "    val_indices.extend(indices[train_size_per_class:train_size_per_class + val_size_per_class])\n",
    "\n",
    "# Create Subset datasets\n",
    "from torch.utils.data import Subset\n",
    "trX = Subset(trainset, train_indices)\n",
    "cvX = Subset(trainset, val_indices)\n",
    "\n",
    "print(f\"Number of training samples: {len(trX)}\")\n",
    "print(f\"Number of cross-validation samples: {len(cvX)}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "9a6cf2cd-60d3-4a47-b846-b091861df41b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "image_channels is 3\n",
      "tensor([1., 0., 0., 0., 0., 0., 0., 0., 0., 0.])\n"
     ]
    },
    {
     "data": {
      "image/png": "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+e++++54y1vecuzv//qv/zomJydP+gtfnU4n+vv7o6+v79jXRkZG4nOf+1z6OObqfEI1IcxPpfvvv/9lf9P4Pe95T1xxxRVxzz33xIc+9KG46qqr4sknn4xbb701Tj/99HjsscdOmFm+fHlccskl8fGPf/zYb0c/8sgjx/1nSrfcckt8/etfj1/4hV+I3//934/zzz8/RkdHY8eOHfG1r30tbrvttpP+jzPOOeeciIgZ/73wFVdcEbfeemvceOONcfHFF8ejjz4at9xyS6xfv/5V+U937rnnnujv7493vetdx347+k1velNcffXVrzhz+eWXx6c//en4tV/7tfit3/qt2LdvX3zqU5864YeFf4+25xNOFUKYn0p/+Id/+LJff/zxx+M3fuM34tlnn43bbrst7rjjjjj77LPjox/9aOzatStuvvnmE2be+973xqZNm+KP/uiP4oknnogNGzbE3XffHddcc82xx5x++unx4IMPxq233hp/+qd/Grt27YqFCxfG+vXr47LLLpvxbm62Afqxj30sjh49Grfffnv8yZ/8SWzcuDFuu+22+NKXvhTf/OY3Z/UcJ3PPPffETTfdFJ/5zGei0+nElVdeGX/2Z38WAwMDrzhzySWXxB133BF//Md/HFdeeWWsXbs2fvM3fzNWrlwZ1113Xeo42p5POFV0muYl/zU98Jpz0003xc033xzPPfdcLF++vPpw4DXDb0cDQBEhDABF/HE0ABRxJwwARYQwABQRwgBQRAgDQJFZ/886brx3e25DM52bi4g4yf/j9qRj+Y15yWNtJ/k7dW1+FS/5Mjst3pX84baYfA38vmK7yyD7frb5nGSv9xbX3il0HTTJ85Od+9fhuVdS/jeVmvrEfzl/xse4EwaAIkIYAIoIYQAoIoQBoIgQBoAiQhgAighhACgihAGgiBAGgCJCGACKCGEAKCKEAaCIEAaAIrNuUerry+V106LBpKZFKV0TNNcb8wUmBc0wbS6DbvYMtXqZFfUwc3sltLoMCtq08q+zTYtSejK9M72x4Fiz57bbadONlmvmm2xxz9nicGfkThgAighhACgihAGgiBAGgCJCGACKCGEAKCKEAaCIEAaAIkIYAIoIYQAoIoQBoIgQBoAiQhgAighhACgy6yrDJtmTlZ371+HcWH5j1FQZtitf/PfLVYG9KHes7doTK2oFK8z1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",
      "text/plain": [
       "<Figure size 600x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "batch_size = 640\n",
    "\n",
    "def one_hot_collate(batch):\n",
    "    data = torch.stack([item[0] for item in batch])\n",
    "    labels = torch.tensor([item[1] for item in batch])\n",
    "    one_hot_labels = torch.zeros(labels.size(0), 10)  # 10 classes in MNIST 【0，1，0，0】\n",
    "    one_hot_labels.scatter_(1, labels.unsqueeze(1), 1)\n",
    "    return data, one_hot_labels\n",
    "\n",
    "trLoader = torch.utils.data.DataLoader(trX, batch_size=batch_size, shuffle=True, num_workers=0, collate_fn=one_hot_collate)\n",
    "cvLoader = torch.utils.data.DataLoader(cvX, batch_size=batch_size, shuffle=False, num_workers=0, collate_fn=one_hot_collate)\n",
    "teLoader = torch.utils.data.DataLoader(testset, batch_size=batch_size, shuffle=False, num_workers=0, collate_fn=one_hot_collate)\n",
    "\n",
    "# Get a batch of training data\n",
    "dataiter = iter(trLoader)\n",
    "data, labels = next(dataiter)\n",
    "\n",
    "image_channels = data[0].numpy().shape[0]\n",
    "print(f'image_channels is {image_channels}')\n",
    "print(labels[0,:])\n",
    "\n",
    "# Label text of CIFAR-10\n",
    "label_text = ['airplane', 'automobile', 'bird', 'cat', 'deer', \n",
    "              'dog', 'frog', 'horse', 'ship', 'truck']\n",
    "\n",
    "# Plot one image from the batch\n",
    "plt.figure(figsize=(6, 6))\n",
    "# Modify the imshow line to handle RGB images correctly\n",
    "plt.imshow(data[0].permute(1, 2, 0).numpy())  # Rearrange from (3,32,32) to (32,32,3)\n",
    "plt.title(f'Label: {label_text[labels[0].argmax().item()]}')\n",
    "plt.axis('off')\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "bfaee1c3-9e45-4857-99a9-9ddad224430e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ResNet(\n",
      "  (conv1): Conv2d(3, 64, kernel_size=(7, 7), stride=(2, 2), padding=(3, 3), bias=False)\n",
      "  (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "  (relu): ReLU(inplace=True)\n",
      "  (maxpool): MaxPool2d(kernel_size=3, stride=2, padding=1, dilation=1, ceil_mode=False)\n",
      "  (layer1): Sequential(\n",
      "    (0): BasicBlock(\n",
      "      (conv1): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "      (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "      (relu): ReLU(inplace=True)\n",
      "      (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "      (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "    )\n",
      "    (1): BasicBlock(\n",
      "      (conv1): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "      (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "      (relu): ReLU(inplace=True)\n",
      "      (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "      (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "    )\n",
      "  )\n",
      "  (layer2): Sequential(\n",
      "    (0): BasicBlock(\n",
      "      (conv1): Conv2d(64, 128, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)\n",
      "      (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "      (relu): ReLU(inplace=True)\n",
      "      (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "      (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "      (downsample): Sequential(\n",
      "        (0): Conv2d(64, 128, kernel_size=(1, 1), stride=(2, 2), bias=False)\n",
      "        (1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "      )\n",
      "    )\n",
      "    (1): BasicBlock(\n",
      "      (conv1): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "      (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "      (relu): ReLU(inplace=True)\n",
      "      (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "      (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "    )\n",
      "  )\n",
      "  (layer3): Sequential(\n",
      "    (0): BasicBlock(\n",
      "      (conv1): Conv2d(128, 256, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)\n",
      "      (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "      (relu): ReLU(inplace=True)\n",
      "      (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "      (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "      (downsample): Sequential(\n",
      "        (0): Conv2d(128, 256, kernel_size=(1, 1), stride=(2, 2), bias=False)\n",
      "        (1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "      )\n",
      "    )\n",
      "    (1): BasicBlock(\n",
      "      (conv1): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "      (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "      (relu): ReLU(inplace=True)\n",
      "      (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "      (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "    )\n",
      "  )\n",
      "  (layer4): Sequential(\n",
      "    (0): BasicBlock(\n",
      "      (conv1): Conv2d(256, 512, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)\n",
      "      (bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "      (relu): ReLU(inplace=True)\n",
      "      (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "      (bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "      (downsample): Sequential(\n",
      "        (0): Conv2d(256, 512, kernel_size=(1, 1), stride=(2, 2), bias=False)\n",
      "        (1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "      )\n",
      "    )\n",
      "    (1): BasicBlock(\n",
      "      (conv1): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "      (bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "      (relu): ReLU(inplace=True)\n",
      "      (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "      (bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "    )\n",
      "  )\n",
      "  (avgpool): AdaptiveAvgPool2d(output_size=(1, 1))\n",
      "  (fc): Linear(in_features=512, out_features=1000, bias=True)\n",
      ")\n",
      "ResNet(\n",
      "  (conv1): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
      "  (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "  (relu): ReLU(inplace=True)\n",
      "  (maxpool): Identity()\n",
      "  (layer1): Sequential(\n",
      "    (0): BasicBlock(\n",
      "      (conv1): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "      (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "      (relu): ReLU(inplace=True)\n",
      "      (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "      (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "    )\n",
      "    (1): BasicBlock(\n",
      "      (conv1): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "      (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "      (relu): ReLU(inplace=True)\n",
      "      (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "      (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "    )\n",
      "  )\n",
      "  (layer2): Sequential(\n",
      "    (0): BasicBlock(\n",
      "      (conv1): Conv2d(64, 128, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)\n",
      "      (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "      (relu): ReLU(inplace=True)\n",
      "      (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "      (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "      (downsample): Sequential(\n",
      "        (0): Conv2d(64, 128, kernel_size=(1, 1), stride=(2, 2), bias=False)\n",
      "        (1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "      )\n",
      "    )\n",
      "    (1): BasicBlock(\n",
      "      (conv1): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "      (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "      (relu): ReLU(inplace=True)\n",
      "      (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "      (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "    )\n",
      "  )\n",
      "  (layer3): Sequential(\n",
      "    (0): BasicBlock(\n",
      "      (conv1): Conv2d(128, 256, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)\n",
      "      (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "      (relu): ReLU(inplace=True)\n",
      "      (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "      (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "      (downsample): Sequential(\n",
      "        (0): Conv2d(128, 256, kernel_size=(1, 1), stride=(2, 2), bias=False)\n",
      "        (1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "      )\n",
      "    )\n",
      "    (1): BasicBlock(\n",
      "      (conv1): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "      (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "      (relu): ReLU(inplace=True)\n",
      "      (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "      (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "    )\n",
      "  )\n",
      "  (layer4): Sequential(\n",
      "    (0): BasicBlock(\n",
      "      (conv1): Conv2d(256, 512, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)\n",
      "      (bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "      (relu): ReLU(inplace=True)\n",
      "      (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "      (bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "      (downsample): Sequential(\n",
      "        (0): Conv2d(256, 512, kernel_size=(1, 1), stride=(2, 2), bias=False)\n",
      "        (1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "      )\n",
      "    )\n",
      "    (1): BasicBlock(\n",
      "      (conv1): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "      (bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "      (relu): ReLU(inplace=True)\n",
      "      (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "      (bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
      "    )\n",
      "  )\n",
      "  (avgpool): AdaptiveAvgPool2d(output_size=(1, 1))\n",
      "  (fc): Linear(in_features=512, out_features=10, bias=True)\n",
      ")\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\ProgramData\\anaconda3\\envs\\machinelearning\\lib\\site-packages\\torchvision\\models\\_utils.py:208: UserWarning: The parameter 'pretrained' is deprecated since 0.13 and may be removed in the future, please use 'weights' instead.\n",
      "  warnings.warn(\n",
      "C:\\ProgramData\\anaconda3\\envs\\machinelearning\\lib\\site-packages\\torchvision\\models\\_utils.py:223: UserWarning: Arguments other than a weight enum or `None` for 'weights' are deprecated since 0.13 and may be removed in the future. The current behavior is equivalent to passing `weights=ResNet18_Weights.IMAGENET1K_V1`. You can also use `weights=ResNet18_Weights.DEFAULT` to get the most up-to-date weights.\n",
      "  warnings.warn(msg)\n"
     ]
    }
   ],
   "source": [
    "import torch.nn as nn\n",
    "\n",
    "# 1. 加载预训练模型\n",
    "model = torchvision.models.resnet18(pretrained=True)\n",
    "print(model)\n",
    "# 2. 修改输入层 (因为 MNIST 是单通道图像)\n",
    "model.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1)\n",
    "\n",
    "# 3. 移除第一层Maxpooling避免参数过早消失\n",
    "model.maxpool = nn.Identity() # nn.Conv2d(64, 64, 1, 1, 1)\n",
    "\n",
    "# 4. 修改输出层 (根据任务的类别数)\n",
    "model.fc = nn.Linear(model.fc.in_features, 10)  # 10为MNIST的类别数\n",
    "\n",
    "# 打印模型结构\n",
    "print(model)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "72c9d00b-3f9f-417f-8a92-2cad3e2fed85",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/5, Training Loss: 2.5553\n",
      "Epoch 1/5, Training Loss: 1.7716\n",
      "Epoch 1/5, Training Loss: 1.6657\n",
      "Epoch 1/5, Training Loss: 1.5981\n",
      "Epoch 1/5, Training Loss: 1.4237\n",
      "Epoch 1/5, Training Loss: 1.2313\n",
      "Epoch 1/5, Training Loss: 1.1601\n",
      "Epoch 1/5, Training Loss: 1.0996\n",
      "Epoch 1/5, Training Loss: 1.1093\n",
      "Epoch 1/5, Training Loss: 1.0077\n",
      "Epoch 1/5, Training Loss: 0.9801\n",
      "Epoch 1/5, Training Loss: 0.9003\n",
      "Epoch 1/5, Training Loss: 0.9413\n",
      "Epoch 1/5, Training Loss: 0.8693\n",
      "Epoch 1/5, Training Loss: 0.8345\n",
      "Epoch 1/5, Training Loss: 0.9464\n",
      "Epoch 1/5, Training Loss: 0.8612\n",
      "Epoch 1/5, Training Loss: 0.7861\n",
      "Epoch 1/5, Training Loss: 0.8320\n",
      "Epoch 1/5, Training Loss: 0.7728\n",
      "Epoch 1/5, Training Loss: 0.7503\n",
      "Epoch 1/5, Training Loss: 0.7770\n",
      "Epoch 1/5, Training Loss: 0.8116\n",
      "Epoch 1/5, Training Loss: 0.7290\n",
      "Epoch 1/5, Training Loss: 0.7097\n",
      "Epoch 1/5, Training Loss: 0.7857\n",
      "Epoch 1/5, Training Loss: 0.7334\n",
      "Epoch 1/5, Training Loss: 0.6392\n",
      "Epoch 1/5, Training Loss: 0.6791\n",
      "Epoch 1/5, Training Loss: 0.6269\n",
      "Epoch 1/5, Training Loss: 0.6810\n",
      "Epoch 1/5, Training Loss: 0.7426\n",
      "Epoch 1/5, Training Loss: 0.5565\n",
      "Epoch 1/5, Training Loss: 0.6340\n",
      "Epoch 1/5, Training Loss: 0.5957\n",
      "Epoch 1/5, Training Loss: 0.6771\n",
      "Epoch 1/5, Training Loss: 0.6195\n",
      "Epoch 1/5, Training Loss: 0.6814\n",
      "Epoch 1/5, Training Loss: 0.6050\n",
      "Epoch 1/5, Training Loss: 0.6048\n",
      "Epoch 1/5, Training Loss: 0.5689\n",
      "Epoch 1/5, Training Loss: 0.6399\n",
      "Epoch 1/5, Training Loss: 0.5885\n",
      "Epoch 1/5, Training Loss: 0.6099\n",
      "Epoch 1/5, Training Loss: 0.5535\n",
      "Epoch 1/5, Training Loss: 0.5382\n",
      "Epoch 1/5, Training Loss: 0.5793\n",
      "Epoch 1/5, Training Loss: 0.5114\n",
      "Epoch 1/5, Training Loss: 0.4862\n",
      "Epoch 1/5, Training Loss: 0.5343\n",
      "Epoch 1/5, Training Loss: 0.5217\n",
      "Epoch 1/5, Training Loss: 0.5149\n",
      "Epoch 1/5, Training Loss: 0.5277\n",
      "Epoch 1/5, Training Loss: 0.4838\n",
      "Epoch 1/5, Training Loss: 0.4696\n",
      "Epoch 1/5, Training Loss: 0.5516\n",
      "Epoch 1/5, Training Loss: 0.5384\n",
      "Epoch 1/5, Training Loss: 0.5037\n",
      "Epoch 1/5, Training Loss: 0.4426\n",
      "Epoch 1/5, Training Loss: 0.5013\n",
      "Epoch 1/5, Training Loss: 0.5181\n",
      "Epoch 1/5, Training Loss: 0.4335\n",
      "Epoch 1/5, Training Loss: 0.4403\n",
      "Epoch 1/5, Training Loss: 0.4403, Cross-Validation Loss: 0.5594\n",
      "Epoch 2/5, Training Loss: 0.3520\n",
      "Epoch 2/5, Training Loss: 0.3696\n",
      "Epoch 2/5, Training Loss: 0.3606\n",
      "Epoch 2/5, Training Loss: 0.3617\n",
      "Epoch 2/5, Training Loss: 0.3525\n",
      "Epoch 2/5, Training Loss: 0.3184\n",
      "Epoch 2/5, Training Loss: 0.3371\n",
      "Epoch 2/5, Training Loss: 0.3337\n",
      "Epoch 2/5, Training Loss: 0.3439\n",
      "Epoch 2/5, Training Loss: 0.3925\n",
      "Epoch 2/5, Training Loss: 0.3755\n",
      "Epoch 2/5, Training Loss: 0.3306\n",
      "Epoch 2/5, Training Loss: 0.3417\n",
      "Epoch 2/5, Training Loss: 0.2935\n",
      "Epoch 2/5, Training Loss: 0.3391\n",
      "Epoch 2/5, Training Loss: 0.3331\n",
      "Epoch 2/5, Training Loss: 0.2988\n",
      "Epoch 2/5, Training Loss: 0.3401\n",
      "Epoch 2/5, Training Loss: 0.2996\n",
      "Epoch 2/5, Training Loss: 0.2631\n",
      "Epoch 2/5, Training Loss: 0.2874\n",
      "Epoch 2/5, Training Loss: 0.3221\n",
      "Epoch 2/5, Training Loss: 0.2816\n",
      "Epoch 2/5, Training Loss: 0.3331\n",
      "Epoch 2/5, Training Loss: 0.3870\n",
      "Epoch 2/5, Training Loss: 0.3055\n",
      "Epoch 2/5, Training Loss: 0.2926\n",
      "Epoch 2/5, Training Loss: 0.2721\n",
      "Epoch 2/5, Training Loss: 0.3513\n",
      "Epoch 2/5, Training Loss: 0.3221\n",
      "Epoch 2/5, Training Loss: 0.3789\n",
      "Epoch 2/5, Training Loss: 0.3422\n",
      "Epoch 2/5, Training Loss: 0.3184\n",
      "Epoch 2/5, Training Loss: 0.3648\n",
      "Epoch 2/5, Training Loss: 0.3390\n",
      "Epoch 2/5, Training Loss: 0.3088\n",
      "Epoch 2/5, Training Loss: 0.3198\n",
      "Epoch 2/5, Training Loss: 0.2892\n",
      "Epoch 2/5, Training Loss: 0.3249\n",
      "Epoch 2/5, Training Loss: 0.3196\n",
      "Epoch 2/5, Training Loss: 0.3046\n",
      "Epoch 2/5, Training Loss: 0.2828\n",
      "Epoch 2/5, Training Loss: 0.3832\n",
      "Epoch 2/5, Training Loss: 0.2548\n",
      "Epoch 2/5, Training Loss: 0.2880\n",
      "Epoch 2/5, Training Loss: 0.2567\n",
      "Epoch 2/5, Training Loss: 0.2711\n",
      "Epoch 2/5, Training Loss: 0.3867\n",
      "Epoch 2/5, Training Loss: 0.3432\n",
      "Epoch 2/5, Training Loss: 0.3617\n",
      "Epoch 2/5, Training Loss: 0.2981\n",
      "Epoch 2/5, Training Loss: 0.2967\n",
      "Epoch 2/5, Training Loss: 0.3455\n",
      "Epoch 2/5, Training Loss: 0.3228\n",
      "Epoch 2/5, Training Loss: 0.3185\n",
      "Epoch 2/5, Training Loss: 0.3278\n",
      "Epoch 2/5, Training Loss: 0.3311\n",
      "Epoch 2/5, Training Loss: 0.3429\n",
      "Epoch 2/5, Training Loss: 0.4409\n",
      "Epoch 2/5, Training Loss: 0.3147\n",
      "Epoch 2/5, Training Loss: 0.3033\n",
      "Epoch 2/5, Training Loss: 0.3136\n",
      "Epoch 2/5, Training Loss: 0.4224\n",
      "Epoch 2/5, Training Loss: 0.4224, Cross-Validation Loss: 0.5957\n",
      "Epoch 3/5, Training Loss: 0.2044\n",
      "Epoch 3/5, Training Loss: 0.1826\n",
      "Epoch 3/5, Training Loss: 0.1914\n",
      "Epoch 3/5, Training Loss: 0.1915\n",
      "Epoch 3/5, Training Loss: 0.1720\n",
      "Epoch 3/5, Training Loss: 0.1600\n",
      "Epoch 3/5, Training Loss: 0.1824\n",
      "Epoch 3/5, Training Loss: 0.2033\n",
      "Epoch 3/5, Training Loss: 0.1661\n",
      "Epoch 3/5, Training Loss: 0.1617\n",
      "Epoch 3/5, Training Loss: 0.1184\n",
      "Epoch 3/5, Training Loss: 0.1669\n",
      "Epoch 3/5, Training Loss: 0.1970\n",
      "Epoch 3/5, Training Loss: 0.1520\n",
      "Epoch 3/5, Training Loss: 0.1350\n",
      "Epoch 3/5, Training Loss: 0.1646\n",
      "Epoch 3/5, Training Loss: 0.1529\n",
      "Epoch 3/5, Training Loss: 0.1791\n",
      "Epoch 3/5, Training Loss: 0.1457\n",
      "Epoch 3/5, Training Loss: 0.1383\n",
      "Epoch 3/5, Training Loss: 0.1736\n",
      "Epoch 3/5, Training Loss: 0.1927\n",
      "Epoch 3/5, Training Loss: 0.1403\n",
      "Epoch 3/5, Training Loss: 0.1182\n",
      "Epoch 3/5, Training Loss: 0.1530\n",
      "Epoch 3/5, Training Loss: 0.1694\n",
      "Epoch 3/5, Training Loss: 0.1762\n",
      "Epoch 3/5, Training Loss: 0.1271\n",
      "Epoch 3/5, Training Loss: 0.1696\n",
      "Epoch 3/5, Training Loss: 0.1293\n",
      "Epoch 3/5, Training Loss: 0.1481\n",
      "Epoch 3/5, Training Loss: 0.1382\n",
      "Epoch 3/5, Training Loss: 0.1069\n",
      "Epoch 3/5, Training Loss: 0.1686\n",
      "Epoch 3/5, Training Loss: 0.1785\n",
      "Epoch 3/5, Training Loss: 0.1630\n",
      "Epoch 3/5, Training Loss: 0.1427\n",
      "Epoch 3/5, Training Loss: 0.1858\n",
      "Epoch 3/5, Training Loss: 0.1341\n",
      "Epoch 3/5, Training Loss: 0.1788\n",
      "Epoch 3/5, Training Loss: 0.1499\n",
      "Epoch 3/5, Training Loss: 0.1530\n",
      "Epoch 3/5, Training Loss: 0.1290\n",
      "Epoch 3/5, Training Loss: 0.1927\n",
      "Epoch 3/5, Training Loss: 0.1564\n",
      "Epoch 3/5, Training Loss: 0.1912\n",
      "Epoch 3/5, Training Loss: 0.2002\n",
      "Epoch 3/5, Training Loss: 0.2335\n",
      "Epoch 3/5, Training Loss: 0.2193\n",
      "Epoch 3/5, Training Loss: 0.1879\n",
      "Epoch 3/5, Training Loss: 0.2146\n",
      "Epoch 3/5, Training Loss: 0.2198\n",
      "Epoch 3/5, Training Loss: 0.2316\n",
      "Epoch 3/5, Training Loss: 0.2050\n",
      "Epoch 3/5, Training Loss: 0.2468\n",
      "Epoch 3/5, Training Loss: 0.1625\n",
      "Epoch 3/5, Training Loss: 0.2011\n",
      "Epoch 3/5, Training Loss: 0.1924\n",
      "Epoch 3/5, Training Loss: 0.2700\n",
      "Epoch 3/5, Training Loss: 0.2112\n",
      "Epoch 3/5, Training Loss: 0.2210\n",
      "Epoch 3/5, Training Loss: 0.1656\n",
      "Epoch 3/5, Training Loss: 0.1868\n",
      "Epoch 3/5, Training Loss: 0.1868, Cross-Validation Loss: 0.4961\n",
      "Epoch 4/5, Training Loss: 0.1107\n",
      "Epoch 4/5, Training Loss: 0.0902\n",
      "Epoch 4/5, Training Loss: 0.1295\n",
      "Epoch 4/5, Training Loss: 0.1306\n",
      "Epoch 4/5, Training Loss: 0.1140\n",
      "Epoch 4/5, Training Loss: 0.1355\n",
      "Epoch 4/5, Training Loss: 0.1086\n",
      "Epoch 4/5, Training Loss: 0.0956\n",
      "Epoch 4/5, Training Loss: 0.1217\n",
      "Epoch 4/5, Training Loss: 0.0874\n",
      "Epoch 4/5, Training Loss: 0.1068\n",
      "Epoch 4/5, Training Loss: 0.0924\n",
      "Epoch 4/5, Training Loss: 0.0920\n",
      "Epoch 4/5, Training Loss: 0.0939\n",
      "Epoch 4/5, Training Loss: 0.0920\n",
      "Epoch 4/5, Training Loss: 0.0742\n",
      "Epoch 4/5, Training Loss: 0.0854\n",
      "Epoch 4/5, Training Loss: 0.0728\n",
      "Epoch 4/5, Training Loss: 0.1351\n",
      "Epoch 4/5, Training Loss: 0.0873\n",
      "Epoch 4/5, Training Loss: 0.0959\n",
      "Epoch 4/5, Training Loss: 0.0890\n",
      "Epoch 4/5, Training Loss: 0.1038\n",
      "Epoch 4/5, Training Loss: 0.0997\n",
      "Epoch 4/5, Training Loss: 0.0815\n",
      "Epoch 4/5, Training Loss: 0.0823\n",
      "Epoch 4/5, Training Loss: 0.0647\n",
      "Epoch 4/5, Training Loss: 0.1068\n",
      "Epoch 4/5, Training Loss: 0.1069\n",
      "Epoch 4/5, Training Loss: 0.0807\n",
      "Epoch 4/5, Training Loss: 0.0753\n",
      "Epoch 4/5, Training Loss: 0.0957\n",
      "Epoch 4/5, Training Loss: 0.1013\n",
      "Epoch 4/5, Training Loss: 0.0879\n",
      "Epoch 4/5, Training Loss: 0.1280\n",
      "Epoch 4/5, Training Loss: 0.0953\n",
      "Epoch 4/5, Training Loss: 0.0871\n",
      "Epoch 4/5, Training Loss: 0.0934\n",
      "Epoch 4/5, Training Loss: 0.1283\n",
      "Epoch 4/5, Training Loss: 0.1060\n",
      "Epoch 4/5, Training Loss: 0.0949\n",
      "Epoch 4/5, Training Loss: 0.1112\n",
      "Epoch 4/5, Training Loss: 0.0881\n",
      "Epoch 4/5, Training Loss: 0.1012\n",
      "Epoch 4/5, Training Loss: 0.0818\n",
      "Epoch 4/5, Training Loss: 0.1011\n",
      "Epoch 4/5, Training Loss: 0.0786\n",
      "Epoch 4/5, Training Loss: 0.1148\n",
      "Epoch 4/5, Training Loss: 0.1283\n",
      "Epoch 4/5, Training Loss: 0.1214\n",
      "Epoch 4/5, Training Loss: 0.1085\n",
      "Epoch 4/5, Training Loss: 0.1040\n",
      "Epoch 4/5, Training Loss: 0.1171\n",
      "Epoch 4/5, Training Loss: 0.1290\n",
      "Epoch 4/5, Training Loss: 0.1198\n",
      "Epoch 4/5, Training Loss: 0.1064\n",
      "Epoch 4/5, Training Loss: 0.1141\n",
      "Epoch 4/5, Training Loss: 0.1348\n",
      "Epoch 4/5, Training Loss: 0.1392\n",
      "Epoch 4/5, Training Loss: 0.1393\n",
      "Epoch 4/5, Training Loss: 0.1402\n",
      "Epoch 4/5, Training Loss: 0.1566\n",
      "Epoch 4/5, Training Loss: 0.1073\n",
      "Epoch 4/5, Training Loss: 0.1073, Cross-Validation Loss: 0.5330\n",
      "Epoch 5/5, Training Loss: 0.0618\n",
      "Epoch 5/5, Training Loss: 0.0569\n",
      "Epoch 5/5, Training Loss: 0.0953\n",
      "Epoch 5/5, Training Loss: 0.1035\n",
      "Epoch 5/5, Training Loss: 0.0831\n",
      "Epoch 5/5, Training Loss: 0.0661\n",
      "Epoch 5/5, Training Loss: 0.0815\n",
      "Epoch 5/5, Training Loss: 0.0684\n",
      "Epoch 5/5, Training Loss: 0.0635\n",
      "Epoch 5/5, Training Loss: 0.0494\n",
      "Epoch 5/5, Training Loss: 0.0755\n",
      "Epoch 5/5, Training Loss: 0.0749\n",
      "Epoch 5/5, Training Loss: 0.0782\n",
      "Epoch 5/5, Training Loss: 0.0859\n",
      "Epoch 5/5, Training Loss: 0.0699\n",
      "Epoch 5/5, Training Loss: 0.0849\n",
      "Epoch 5/5, Training Loss: 0.0978\n",
      "Epoch 5/5, Training Loss: 0.0665\n",
      "Epoch 5/5, Training Loss: 0.0774\n",
      "Epoch 5/5, Training Loss: 0.0714\n",
      "Epoch 5/5, Training Loss: 0.0516\n",
      "Epoch 5/5, Training Loss: 0.0805\n",
      "Epoch 5/5, Training Loss: 0.0657\n",
      "Epoch 5/5, Training Loss: 0.0894\n",
      "Epoch 5/5, Training Loss: 0.0631\n",
      "Epoch 5/5, Training Loss: 0.0837\n",
      "Epoch 5/5, Training Loss: 0.0664\n",
      "Epoch 5/5, Training Loss: 0.1122\n",
      "Epoch 5/5, Training Loss: 0.0884\n",
      "Epoch 5/5, Training Loss: 0.0896\n",
      "Epoch 5/5, Training Loss: 0.0608\n",
      "Epoch 5/5, Training Loss: 0.0693\n",
      "Epoch 5/5, Training Loss: 0.0684\n",
      "Epoch 5/5, Training Loss: 0.0734\n",
      "Epoch 5/5, Training Loss: 0.0914\n",
      "Epoch 5/5, Training Loss: 0.0736\n",
      "Epoch 5/5, Training Loss: 0.0504\n",
      "Epoch 5/5, Training Loss: 0.0752\n",
      "Epoch 5/5, Training Loss: 0.0769\n",
      "Epoch 5/5, Training Loss: 0.0507\n",
      "Epoch 5/5, Training Loss: 0.0938\n",
      "Epoch 5/5, Training Loss: 0.0807\n",
      "Epoch 5/5, Training Loss: 0.0684\n",
      "Epoch 5/5, Training Loss: 0.0372\n",
      "Epoch 5/5, Training Loss: 0.0726\n",
      "Epoch 5/5, Training Loss: 0.0760\n",
      "Epoch 5/5, Training Loss: 0.0709\n",
      "Epoch 5/5, Training Loss: 0.0760\n",
      "Epoch 5/5, Training Loss: 0.0584\n",
      "Epoch 5/5, Training Loss: 0.0879\n",
      "Epoch 5/5, Training Loss: 0.0696\n",
      "Epoch 5/5, Training Loss: 0.0750\n",
      "Epoch 5/5, Training Loss: 0.0727\n",
      "Epoch 5/5, Training Loss: 0.0737\n",
      "Epoch 5/5, Training Loss: 0.0524\n",
      "Epoch 5/5, Training Loss: 0.1196\n",
      "Epoch 5/5, Training Loss: 0.0915\n",
      "Epoch 5/5, Training Loss: 0.0552\n",
      "Epoch 5/5, Training Loss: 0.0722\n",
      "Epoch 5/5, Training Loss: 0.0909\n",
      "Epoch 5/5, Training Loss: 0.0719\n",
      "Epoch 5/5, Training Loss: 0.0796\n",
      "Epoch 5/5, Training Loss: 0.0870\n",
      "Epoch 5/5, Training Loss: 0.0870, Cross-Validation Loss: 0.5828\n"
     ]
    }
   ],
   "source": [
    "# 定义损失函数\n",
    "criterion = nn.CrossEntropyLoss()\n",
    "\n",
    "# 只优化未冻结的参数\n",
    "# optimizer = torch.optim.Adam(filter(lambda p: p.requires_grad, model.parameters()))\n",
    "optimizer = torch.optim.Adam(model.parameters())\n",
    "\n",
    "# 训练模型\n",
    "num_epochs = 5\n",
    "train_losses = []\n",
    "cv_losses = []\n",
    "\n",
    "for epoch in range(num_epochs):\n",
    "    model.train()\n",
    "    for images, labels in trLoader:\n",
    "        optimizer.zero_grad()\n",
    "        outputs = model(images)\n",
    "        loss = criterion(outputs, labels)\n",
    "        loss.backward()\n",
    "        optimizer.step()\n",
    "        \n",
    "        print(f'Epoch {epoch+1}/{num_epochs}, Training Loss: {loss.item():.4f}')\n",
    "        \n",
    "    train_losses.append(loss.item())\n",
    "\n",
    "    # 计算交叉验证损失\n",
    "    model.eval()\n",
    "    cv_loss = 0.0\n",
    "    with torch.no_grad():\n",
    "        for images, labels in cvLoader:\n",
    "            outputs = model(images)\n",
    "            loss = criterion(outputs, labels)\n",
    "            cv_loss += loss.item()\n",
    "    cv_losses.append(cv_loss / len(cvLoader))\n",
    "\n",
    "    print(f'Epoch {epoch+1}/{num_epochs}, Training Loss: {train_losses[-1]:.4f}, Cross-Validation Loss: {cv_losses[-1]:.4f}')\n",
    "\n",
    "# 保存模型\n",
    "torch.save(model.state_dict(), 'mnist_resnet18_finetuned.pth')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "b2b14e45-37a4-4019-bc7f-103c1a66c43f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Accuracy on training set: 94.41%\n",
      "Accuracy on cross-validation set: 84.49%\n"
     ]
    },
    {
     "data": {
      "image/png": "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ccqUKWJgYKBoa2sr2tnZiW3bthUXL15s7B546NAhcfjw4aKfn5+oVqtFV1dXsVevXuKvv/5ars8iiqKYn58venp6mu3wJooPdp0Vv55EURRv3rwpjhw50uSaiI6OLvF6hfs5OzuL9vb24sCBA8V//vlH9PPzEydPnmzymkuWLBH9/f1FuVxu8jrm/l5zc3PFV199VfTz8xOVSqXo7e0tPvfcc+Ldu3dN9ivv90hpAJT6KLwmU1JSxOnTp4ve3t6iQqEQ/fz8xLlz54p5eXnG1/n999/FQYMGiY0bNxZVKpXo4eEhDh48WDx48KBxn0WLFondunUT3dzcRJVKJfr6+opTp04Vr1+/XmadRFS/CaJYZI4FERHVKR988AHefPNNxMbGokmTJlKXQ0REVG9xqh4RUR3x5ZdfAgACAwOh1WqxZ88efPHFF3jiiScYmoiIiKoZgxMRUR1hY2ODxYsX4/r169BoNPD19cWrr76KN998U+rSiIiI6j1O1SMiIiIiIioD25ETERERERGVgcGJiIiIiIioDAxOREREREREZWhwzSH0ej1u374Ne3t74yruRERERETU8IiiiMzMTDRq1KjMxeQbXHC6ffu2yUruRERERETUsMXFxZW5tEeDC0729vYADCfHwcFB4mqIiIiIiEgqGRkZ8PHxMWYESxpccCqcnufg4MDgRERERERE5bqFh80hiIiIiIiIysDgREREREREVAYGJyIiIiIiojIwOBEREREREZWBwYmIiIiIiKgMDE5ERERERERlYHAiIiIiIiIqg+TBaenSpfD394eVlRXCwsJw8OBBi/trNBq88cYb8PPzg1qtRvPmzbFy5coaqpaIiIiIiBoiSRfA3bBhA2bPno2lS5eie/fuWL58OQYNGoSYmBj4+vqaPWb06NFITEzEt99+ixYtWiApKQkFBQU1XDkRERERETUkgiiKolRv3qVLF4SGhiIyMtK4LSgoCMOGDcPChQtL7L99+3aMHTsWV69ehYuLS6XeMyMjA46OjkhPT4eDg0OlayciIiIiorqtItlAsql6+fn5OH78OCIiIky2R0REIDo62uwxv/76Kzp27IiPP/4YjRs3RqtWrfDyyy8jNze31PfRaDTIyMgweRAREREREVWEZFP1kpOTodPp4OnpabLd09MTCQkJZo+5evUq/vzzT1hZWWHLli1ITk7G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q9VqqNXqStdJRER1mMrGEJDajwOSL91ra77O0Nb8r8/NHyNTGgJViUVgmwOOTQCZvEY/AhER1Q6SBSeVSoWwsDBERUVh+PDhxu1RUVEYOnRouV/n5MmT8Pb2ro4SiYioPnFrCfRfADz8FvDvDuDMBkCbW2Qh2Hv3Hzn6AHJJm84SEVEtJOm/DHPmzMHEiRPRsWNHhIeHY8WKFYiNjcX06dMBAHPnzsWtW7ewZs0aAMCSJUvQtGlThISEID8/H99//z02b96MzZs3S/kxiIioLpErgaBHDA8iIqJykjQ4jRkzBikpKViwYAHi4+PRunVrbNu2DX5+fgCA+Ph4xMbGGvfPz8/Hyy+/jFu3bsHa2hohISHYunUrBg8eLNVHICIiIiKiBkDSdZykwHWciIiIiIgIqFg2YPsfIiIiIiKiMjA4ERERERERlYHBiYiIiIiIqAwMTkRERERERGVgcCIiIiIiIioDgxMREREREVEZGJyIiIiIiIjKwOBERERERERUBgYnIiIiIiKiMjA4ERERERERlYHBiYiIiIiIqAwMTkRERERERGVgcCIiIiIiIioDgxMREREREVEZGJyIiIiIiIjKwOBERERERERUBgYnIiIiIiKiMjA4ERERERERlYHBiYiIiIiIqAwMTkRERERERGVgcCIiIiIiIioDgxMREREREVEZGJyIiIiIiIjKwOBERERERERUBgYnIiIiIiKiMjA4ERERERERlYHBiYiIiIiIqAwMTkRERERERGVgcCIiIiIiIioDgxMREREREVEZGJyIiIiIiIjKwOBERERERERUBgYnIiIiIiKiMiikLqAh23LyJt77/TwcbZRwslbCyUYFJ2vlva9VcLJRwslGCccizznZKGFvpYRcJkhdPhERERFRg8HgJKGUrHykZBseFSEIgL1aYQhTZoJV8a+dbJRwsDaEMZWCg4xERERERBXF4CShxzv64KGWbkjL0SItR4v03HzDn3Pvf51+78+Gr7XI0hRAFIGMvAJk5BUgNrVi72mjkt8b1TINVo6FI1zWpkGr8HlrpRyCwFEuIiIiImqYGJwk5GhtGB2qCK1ObwxTxqB1L2yl59wLWrmm29JyDaFLFIGcfB1y8nW4nZ5XofdVyWVFphSWDFqOxcOYtQqONkrYqxWQcVohEREREdVxDE51jFIug5udGm526godp9eLyMwr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",
      "text/plain": [
       "<Figure size 1000x500 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Calculate and print accuracies for training and cross-validation sets\n",
    "model.eval()\n",
    "with torch.no_grad():\n",
    "    # Training set accuracy\n",
    "    tr_correct = 0\n",
    "    tr_total = 0\n",
    "    for images, labels in trLoader:\n",
    "        outputs = model(images)\n",
    "        _, predicted = torch.max(outputs, 1)\n",
    "        _, true_labels = torch.max(labels, 1)\n",
    "        tr_total += labels.size(0)\n",
    "        tr_correct += (predicted == true_labels).sum().item()\n",
    "    \n",
    "    tr_accuracy = 100 * tr_correct / tr_total\n",
    "    \n",
    "    # Test set accuracy\n",
    "    cv_correct = 0\n",
    "    cv_total = 0\n",
    "    for images, labels in cvLoader:\n",
    "        outputs = model(images)\n",
    "        _, predicted = torch.max(outputs, 1)\n",
    "        _, true_labels = torch.max(labels, 1)\n",
    "        cv_total += labels.size(0)\n",
    "        cv_correct += (predicted == true_labels).sum().item()\n",
    "    \n",
    "    cv_accuracy = 100 * cv_correct / cv_total\n",
    "\n",
    "print(f'Accuracy on training set: {tr_accuracy:.2f}%')\n",
    "print(f'Accuracy on cross-validation set: {cv_accuracy:.2f}%')\n",
    "\n",
    "# Plot training and cross-validation losses\n",
    "plt.figure(figsize=(10, 5))\n",
    "plt.plot(range(1, num_epochs+1), train_losses, label='Training Loss')\n",
    "plt.plot(range(1, num_epochs+1), cv_losses, label='Cross-Validation Loss')\n",
    "plt.xlabel('Epoch')\n",
    "plt.ylabel('Loss')\n",
    "plt.title('Training and Cross-Validation Loss')\n",
    "plt.legend()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "3982bd64-dda4-40bc-9278-94a5dc73ab5d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Accuracy on test set: 84.06%\n"
     ]
    }
   ],
   "source": [
    "model.eval()\n",
    "with torch.no_grad():\n",
    "    test_correct = 0\n",
    "    test_total = 0\n",
    "    for images, labels in teLoader:\n",
    "        outputs = model(images)\n",
    "        _, predicted = torch.max(outputs, 1)\n",
    "        _, true_labels = torch.max(labels, 1)\n",
    "        test_total += labels.size(0)\n",
    "        test_correct += (predicted == true_labels).sum().item()\n",
    "    test_accuracy = 100 * test_correct / test_total\n",
    "    print(f'Accuracy on test set: {test_accuracy:.2f}%')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f4c980fc-e200-445b-8707-ac9ac4d9ec17",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
